#include "stk_message_http.h"
#include "stk_utility.h"
#include "stk_const.h"
#include <string.h>
#include "stk_cors_header.h"
#include "stk_cors_method.h"

using namespace stk;

/* 构造 */
HttpMsg::HttpMsg()
{
    init();
}

HttpMsg::HttpMsg(const char *str, int32_t len)
{
    init();

    if (!str || len == 0)
    {
        return;
    }

    parse(str, len);
}

HttpMsg::HttpMsg(const HttpMsg &src)
{
    if (this == &src)
    {
        return;
    }

    init();

    *this = src;
}

HttpMsg::~HttpMsg()
{
    clear();
}

HttpMsg & HttpMsg::operator= (const HttpMsg &src)
{
    if (this == &src)
    {
        return *this;
    }

    clear();

    /* 需显示调用基类拷贝构造 */
    MsgBase::operator=(src);

    hosts.assign(src.hosts, _override_t<Host>());
    connections.assign(src.connections, _override_t<Connection>());

    return *this;
}

void HttpMsg::init()
{
    m_iMsgType = MSG_TYPE_HTTP;
}

void HttpMsg::clear()
{
    hosts.clear();
    connections.clear();
}

int32_t	HttpMsg::to_str(string &str)
{
    int32_t ret = 0;

    str = "";

    if (1 == m_iMessageProperty)
    {
        str = m_strMessage;

        return 0;
    }
    else
    {
        m_strMessage = "";
    }

    if (MakeStartLineString(str) < 0)
    {
        return -1;
    }

    str += CRLF;

    string strColon = ": ";

    string strFieldHeader = STK_HTTP_HOST + strColon;
    ret += MakeSingleLineFieldString(hosts, strFieldHeader, str);

    strFieldHeader = STK_COMM_ACCEPT_ENCODING + strColon;
    ret += MakeSingleLineFieldString(accept_encodings, strFieldHeader, str);

    strFieldHeader = STK_COMM_CONTENT_ENCODING + strColon;
    ret += MakeFieldString(content_encoding, strFieldHeader, str);

    strFieldHeader = STK_COMM_CONTENT_TYPE + strColon;
    ret += MakeFieldString(content_type, strFieldHeader, str);

    strFieldHeader = STK_COMM_SERVER + strColon;
    ret += MakeFieldString(server, strFieldHeader, str);

    strFieldHeader = STK_COMM_PUBLIC + strColon;
    ret += MakeSingleLineFieldString(publics, strFieldHeader, str);

    strFieldHeader = STK_COMM_ALLOW + strColon;
    ret += MakeSingleLineFieldString(allows, strFieldHeader, str);

    strFieldHeader = STK_HTTP_CONNECTION + strColon;
    ret += MakeSingleLineFieldString(connections, strFieldHeader, str);

    strFieldHeader = STK_HTTP_ORIGIN + strColon;
    ret += MakeFieldString(origin, strFieldHeader, str);

    strFieldHeader = STK_HTTP_CORS_ORIGIN + strColon;
    ret += MakeFieldString(cors_orgin, strFieldHeader, str);

    strFieldHeader = STK_HTTP_CORS_HEADERS + strColon;
    ret += MakeSingleLineFieldString(cors_headers, strFieldHeader, str);

    strFieldHeader = STK_HTTP_CORS_METHODS + strColon;
    ret += MakeSingleLineFieldString(cors_methods, strFieldHeader, str);

    strFieldHeader = STK_HTTP_WEBSOCK_KEY + strColon;
    ret += MakeFieldString(websock_key, strFieldHeader, str);

    strFieldHeader = STK_HTTP_WEBSOCK_VERSION + strColon;
    ret += MakeFieldString(websock_version, strFieldHeader, str);

    strFieldHeader = STK_HTTP_WEBSOCK_ACCPET + strColon;
    ret += MakeFieldString(websock_accept, strFieldHeader, str);

    strFieldHeader = STK_HTTP_WEBSOCK_EXTENSIONS + strColon;
    ret += MakeFieldString(websock_extensions, strFieldHeader, str);

    strFieldHeader = STK_HTTP_UPGRADE + strColon;
    ret += MakeFieldString(upgrade, strFieldHeader, str);

    if (ret < 0)
    {
        return -1;
    }

    /* we have to create the body before adding the contentlength */
    /* add enough lenght for "Content-Length: " */

    strFieldHeader = STK_COMM_CONTENT_LENGTH + strColon;
    str += strFieldHeader;

    if (bodies.eol(0))                     /* no body */
    {
        str += "0\r\n\r\n";
        m_iMessageProperty = 1;
        this->m_strMessage = str;
        return 0;                          /* it's all done */
    }

    string strBoundary;
    string strBodyBuffer = CRLF;

    if (!content_type.m_strType.empty() &&
            !strcasecmp(content_type.m_strType.c_str(), "multipart") &&
            bodies.size() >= 2)
    {
#if 0
        /* find the boundary */
        MsgParam *ct_param = content_type.m_listParams.get_byname("boundary");

        if (ct_param && !ct_param->m_strValue.empty())
        {
            size_t len = ct_param->m_strValue.size();

            const int MIME_MAX_BOUNDARY_LEN = 70;

            if (len > MIME_MAX_BOUNDARY_LEN)
            {

                printf("ERRO: <MakeToString> len > MIME_MAX_BOUNDARY_LEN!\n");
                return -1;
            }

            strBoundary =  "--";

            if (len >= 2 && ct_param->m_strValue[0] == '"' && ct_param->m_strValue[len - 1] == '"')
            {
                strBoundary.append(ct_param->m_strValue.c_str() + 1, len - 2);
            }
            else
            {
                strBoundary.append(ct_param->m_strValue.c_str(), len);
            }
        }

#endif
    }

    int pos = 0;

    while (!bodies.eol(pos))
    {
        Body *body = (Body *)bodies.get(pos++);

        if (body == NULL)
        {
            continue;
        }

        string strThisBoundary = strBoundary;

        if (!strThisBoundary.empty())
        {
#if 0
            char szThisBodySize[20] = {0};

            if (pos > 1)
            {
                strThisBoundary = string(CRLF) + strThisBoundary;
            }

            strThisBoundary += CRLF;
            snprintf(szThisBodySize, sizeof(szThisBodySize), "%d\r\n", body->m_strBody.size());
            strThisBoundary += strContentLengthHeader + szThisBodySize ;
            strThisBoundary += strContentTypeHeader;
            body->m_oContentType.to_str(strThisBoundary);
            strThisBoundary += + CRLF ;
            strBodyBuffer += strThisBoundary + CRLF;
#endif
        }

        if (body->to_str(strBodyBuffer) != 0)
        {

            printf("ERRO: <MakeToString> parse body fail!\n");
            return -1;
        }
    }

    if (!strBoundary.empty())
    {
        /* Needs at most 79 bytes,
        last realloc allocate at least 100 bytes extra */
        strBodyBuffer += CRLF + strBoundary + "--" /*+ CRLF*/;
    }

    if (strBodyBuffer.size() <= 2)
    {

        printf("ERRO: <MakeToString> body buffer length <=2!\n");
        return -1;
    }

    char szBodyLen[256] = "";
    snprintf(szBodyLen, sizeof(szBodyLen) - 1, "%ld\r\n", strBodyBuffer.size() - 2);
    str = str + szBodyLen + strBodyBuffer;

    /* same remark as at the beginning of the method */
    m_iMessageProperty = 1;
    m_strMessage       = str;

    return 0;
}

int32_t HttpMsg::parse(const char *str, int32_t len)
{
    if (len <= 0 || len >= MAX_MESSAGE_SIZE)
    {
        printf("HttpMsg::parse buffer size is 0 or exceeded max size(%d)\n", MAX_MESSAGE_SIZE);

        return -1;
    }

    const char *next_header_index;

    char beg[MAX_MESSAGE_SIZE];
    char *tmp = beg;
    memcpy(tmp, str, len);    /* may contain binary data */
    tmp[len] = '\0';

    stk_util_replace_all_lws(tmp);

    /* parse request or status line */
    if (startline_parse(tmp, &next_header_index) != 0)
    {
        /* printf("HttpMsg::parse:Can not parse start line.message content=<%s>\n", tmp); */
        return -1;
    }

    tmp = (char *)next_header_index;

    /* parse headers */
    if (msg_headers_parse(tmp, &next_header_index) != 0)
    {
        /* printf("HttpMsg::parse:Can not parse message headers.message content=<%s>\n", tmp); */
        return -1;
    }

    tmp = (char *)next_header_index;

    /* this is a *very* simple test... (which handle most cases...) */
    if (tmp[0] == '\0' || tmp[1] == '\0' || tmp[2] == '\0')
    {
        m_bValid = true;
        return 0;                 /* no body found */
        //return SetMsgIdByMethodName();
    }

    if (msg_body_parse(tmp, &next_header_index, len - (tmp - beg)) != 0)
    {
        /* printf("HttpMsg::parse:Can not parse message body.message content=<%s>\n", tmp); */
        return -1;
    }

    m_bValid = true;

    return 0;
}

int32_t HttpMsg::startline_parse(const char *buf, const char **headers)
{
    if (0 == strncmp((const char *) buf, (const char *) "HTTP/", 5))
    {
        return startline_parseresp(buf, headers);
    }
    else
    {
        return startline_parsereq(buf, headers);
    }
}

int32_t	HttpMsg::startline_parsereq(const char *buf, const char **headers)
{
    const char *p1;
    const char *p2;

    m_strMethod   = "";
    m_iStatusCode = 0;

    *headers = buf;

    /* The first token is the method name: */
    p2 = strchr(buf, ' ');

    if (p2 == NULL)
    {
        return -1;
    }

    if (*(p2 + 1) == '\0' || *(p2 + 2) == '\0')
    {
        return -1;
    }

    if (p2 - buf == 0)
    {
        printf(("No space allowed here\n"));
        return -1;
    }

    m_strMethod.assign(buf, p2 - buf);

    /* The second token is a sip-url or a uri: */
    p1 = strchr(p2 + 2, ' ');     /* no space allowed inside sip-url */

    if (p1 == NULL)
    {
        printf(("Uncompliant request-uri\n"));
        m_strMethod = "";
        return -1;
    }

    if (p1 - p2 < 2)
    {
        m_strMethod = "";
        return -1;
    }

    m_strReqUrl.assign(p2 + 1, (p1 - p2 - 1));


    /* find the the version and the beginning of headers */
    {
        const char *hp = p1;


        while ((*hp != '\r') && (*hp != '\n'))
        {
            if (*hp)
            {
                hp++;
            }
            else
            {
                printf(("No crlf found\n"));
                m_strMethod = "";
                m_strReqUrl = "";
                return -1;
            }
        }

        if (hp - p1 < 2)
        {
            m_strMethod = "";
            m_strReqUrl = "";
            return -1;
        }

        m_strVersion.assign(p1 + 1, hp - p1 - 1);

        if (!strcasecmp(m_strVersion.c_str(), "HTTP/1.0") &&
                !strcasecmp(m_strVersion.c_str(), "HTTP/1.1"))
        {
            printf(("Wrong version number\n"));
        }

        hp++;

        if ((*hp) && ('\r' == hp[-1]) && ('\n' == hp[0]))
        {
            hp++;
        }

        (*headers) = hp;

    }

    return 0;
}

int32_t	HttpMsg::startline_parseresp(const char *buf, const char **headers)
{
    const char *statuscode;
    const char *reasonphrase;

    m_strReqUrl = "";
    m_strMethod = "";

    *headers = buf;

    statuscode = strchr(buf, ' ');        /* search for first SPACE */

    if (statuscode == NULL)
    {
        return -1;
    }

    string	strVersion = "";
    strVersion.assign(buf, statuscode - *headers);

    if (strcmp(strVersion.c_str(), "HTTP/1.0") != 0 &&
            strcmp(strVersion.c_str(), "HTTP/1.1") != 0)
    {
        return -1;
    }

    m_strVersion = strVersion;

    reasonphrase = strchr(statuscode + 1, ' ');

    if (reasonphrase == NULL)
    {
        m_strVersion = "";
        return -1;
    }

    if (sscanf(statuscode + 1, "%d", &m_iStatusCode) != 1)
    {
        /* Non-numeric status code */
        return -1;
    }

    if (m_iStatusCode == 0)
    {
        return -1;
    }

    {
        const char *hp = reasonphrase;

        while ((*hp != '\r') && (*hp != '\n'))
        {
            if (*hp)
            {
                hp++;
            }
            else
            {
                printf("No crlf found\n");
                return -1;
            }
        }

        m_strReasonPhrase.assign(reasonphrase + 1, hp - reasonphrase - 1);

        hp++;

        if ((*hp) && ('\r' == hp[-1]) && ('\n' == hp[0]))
        {
            hp++;
        }

        (*headers) = hp;
    }

    return 0;
}



int32_t HttpMsg::SetHost(const char *szValue)
{
    if (szValue == NULL || szValue[0] == '\0')
    {
        return 0;
    }

    Host *host = new Host(szValue, strlen(szValue));

    if (NULL == host)
    {
        return -1;
    }

    if (hosts.add(host) != 0)
    {
        delete host;
        return -1;
    }

    m_iMessageProperty = 2;

    return 0;
}

int32_t HttpMsg::SetHost(const Host &field)
{
    if (!field.isvalid())
    {
        return -1;
    }

    Host *host = new Host(field);

    if (!host)
    {
        return -1;
    }

    bodies.add(host);
    m_iMessageProperty = 2;

    return 0;
}

string HttpMsg::GetHost()
{
    string str = "";

    MsgField *field = (MsgField *)hosts.get(0);

    if (field)
    {
        field->to_str(str);
    }

    return str;
}

Host *HttpMsg::GetHost(int32_t pos)
{
    if (hosts.size() <= 0 ||
            (pos < 0 && pos != -1) ||
            pos >= hosts.size())
    {
        return NULL;
    }

    if (-1 == pos)
    {
        return (Host *)hosts.get(hosts.size() - 1);
    }

    return (Host *)hosts.get(pos);
}

int32_t HttpMsg::SetConnection(const char *szValue)
{
    if (szValue == NULL || szValue[0] == '\0')
    {
        return 0;
    }

    Connection *connection = new Connection(szValue);

    if (NULL == connection)
    {
        return -1;
    }

    if (connections.add(connection) != 0)
    {
        delete connection;
        return -1;
    }

    m_iMessageProperty = 2;

    return 0;
}

int32_t HttpMsg::SetConnection(const Connection &field)
{
    if (!field.isvalid())
    {
        return -1;
    }

    Connection *connection = new Connection(field);

    if (!connection)
    {
        return -1;
    }

    bodies.add(connection);
    m_iMessageProperty = 2;

    return 0;
}

string HttpMsg::GetConnection()
{
    string str = "";

    MsgField *field = (MsgField *)connections.get(0);

    if (field)
    {
        field->to_str(str);
    }

    return str;
}

Connection *HttpMsg::GetConnection(int32_t pos)
{
    if (connections.size() <= 0 ||
            (pos < 0 && pos != -1) ||
            pos >= connections.size())
    {
        return NULL;
    }

    if (-1 == pos)
    {
        return (Connection *)connections.get(connections.size() - 1);
    }

    return (Connection *)connections.get(pos);
}

int32_t HttpMsg::SetLocation(const char *szValue)
{
    m_iMessageProperty = 2;
    return 0;
}

int32_t
HttpMsg::SetOrigin(const char *szValue)
{
    if (origin.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

string
HttpMsg::GetOrigin()
{
    string str;
    origin.to_str(str);
    return str;
}

int32_t
HttpMsg::SetCorsOrigin(const char *szValue)
{
    if (cors_orgin.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

int32_t
HttpMsg::SetCorsHeader(const char *szValue)
{
    if (szValue == NULL || szValue[0] == '\0')
    {
        return 0;
    }

    CorsHeader* cors_header = new CorsHeader(szValue);
    if (NULL == cors_header)
    {
        return -1;
    }

    if (cors_headers.add(cors_header) != 0)
    {
        delete cors_header;
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

int32_t
HttpMsg::SetCorsMethod(const char *szValue)
{
    if (szValue == NULL || szValue[0] == '\0')
    {
        return 0;
    }

    CorsMethod* cors_method = new CorsMethod(szValue);
    if (NULL == cors_method)
    {
        return -1;
    }

    if (cors_methods.add(cors_method) != 0)
    {
        delete cors_method;
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

int32_t
HttpMsg::SetUpgrade(const char *szValue)
{
    if (upgrade.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

string
HttpMsg::GetUpgrade()
{
    string str;
    upgrade.to_str(str);
    return str;
}

int32_t
HttpMsg::SetWebsockKey(const char *szValue)
{
    if (websock_key.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

string
HttpMsg::GetWebsockKey()
{
    string str;
    websock_key.to_str(str);
    return str;
}

int32_t
HttpMsg::SetWebsockVersion(const char *szValue)
{
    if (websock_version.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

string
HttpMsg::GetWebsockVersion()
{
    string str;
    websock_version.to_str(str);
    return str;
}

int32_t
HttpMsg::SetWebsockAccept(const char *szValue)
{
    if (websock_accept.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

string
HttpMsg::GetWebsockAccept()
{
    string str;
    websock_accept.to_str(str);
    return str;
}

int32_t
HttpMsg::SetWebsockExtensions(const char *szValue)
{
    if (websock_extensions.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

string
HttpMsg::GetWebsockExtensions()
{
    string str;
    websock_extensions.to_str(str);
    return str;
}
